#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/wait.h>
#include<sys/types.h>
#include<assert.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>

#define COM_SIZE 1024
#define ARGV_SIZE 64
#define DEF_CHAR " "

void split(char* argv[ARGV_SIZE], char* ps)
{
    assert(argv && ps);

    //分割字符串
    int pos = 0;
    argv[pos++] = strtok(ps,DEF_CHAR);//有空格就分割
    while(argv[pos++]=strtok(NULL,DEF_CHAR)); //不断分割

    argv[pos] = NULL ; //确保安全
}

void showEnv()
{
   extern char** environ; //使用当前的环境变量表
   int pos = 0;
   for(;environ[pos];printf("%s\n",environ[pos++]));
}

//枚举类型，用于判断不同的文件打开方式
enum redir
{
    REDIR_INPUT = 0,//读取
    REDIR_OUTPUT,
    REDIR_APPEND,
    REDIR_NONE
}redir_type = REDIR_NONE;// 创捷对象redir_type，默认为 NONE

//检查是否出现重定向
char* checkDir(char* command)
{
    //从右往左遍历
    size_t end = strlen(command);
    char* ps=command +end;
    while(end != 0)
    {
        if(command[end-1] == '>')
        {
            if(command[end-2] == '>')
            {
                command[end-2] = '\0';
                redir_type =REDIR_APPEND;//追加重定向
                return ps;
            }
        }
        else if(command[end-1] == '<')
        {
            command[end-1] = '\0';
            redir_type =REDIR_INPUT;
            return ps;
        }


        //如果不是空格，就可以更新 ps指向
        if(*(command+end-1)!=' ')
            ps=command+end-1;

        end--;
    }

    return NULL;//如果没有重定向符，就返回空


}

int main()
{
    char myEnv[COM_SIZE][ARGV_SIZE];
    int env_pos = 0;//专门维护缓冲区
    int exit_code =0;//保存退出码的全局变量
    //这是一个始终运行的程序:bash
    while(1)
    {
        char command[COM_SIZE];//存放指令的数组（缓冲区）

        //打印提示符
        printf("[User@myBash default]$ ");
        fflush(stdout);
    

    //读取指令
    //因为右空格，所以需要逐行读取
    fgets(command,COM_SIZE,stdin);
    assert(command);//不能输入空指令
    (void)command; //防止在Release版本中出错

    command[strlen(command)-1]='\0';  //将最后一个字符\n变成\0

    char* filename =checkDir(command);

    //指令分割
    char* argv[ARGV_SIZE];
    split(argv,command);

    //特殊处理
    ////颜色高亮，识别是否为 ls 
    if(strcmp(argv[0],"ls") == 0)
    {
        int pos = 0;
        while(argv[pos++]); //找到胃
        argv[pos - 1 ] =(char*)"--color=auto";//添加此字段
        argv[pos] = NULL;//结尾
    }

    //目录间移动处理:
    if(strcmp(argv[0],"cd") == 0)
    {
        //直接调用接口,然后continue 不再执行后续代码
        if(strcmp(argv[1],"~")== 0)
        {
            chdir("/home");//回到家目录
        }
        else if(strcmp(argv[1],"-")== 0)
        {
            chdir(getenv("OLDPWD"));//获取上一次路径
        }
        else if(argv[1])
            chdir(argv[1]); //argv[1]中是路径

        continue;//终止此次循环
    }

    //环境变量相关
    if(strcmp(argv[0],"export") == 0)
    {
        if(argv[1])
        {
            strcpy(myEnv[env_pos],argv[1]);
            putenv(myEnv[env_pos++]);
        }

        continue; //提前结束本轮循环
    }

    //环境变量表
    if(strcmp(argv[0],"env") == 0)
    {
        showEnv(); //调用函数，打印父进程的环境变量表
        continue;
    }

    //echo相关
    //只有 echo $ 才做特殊处理（环境变量加退出码)
    if(strcmp(argv[0],"echo") == 0 && argv[1][0] == '$')
    {
        if(argv[1] && argv[1][0] == '$')
        {
            if(argv[1][1] == '?')
            {
                printf("%d\n",exit_code);
            }
            else
                printf("%s\n",getenv(argv[1]+1));
        }

        continue;
    }

    //子进程进行程序替换
    pid_t id =fork();
    if(id == 0)
    {
        //判断是否需要进行重定向
        if(redir_type == REDIR_INPUT)
        {
            int fd =open(filename ,O_RDONLY);

            dup2(fd, 0);//更换输入，读取文件filename
        }
        else if(redir_type ==REDIR_OUTPUT)
        {
            int fd=open(filename,O_WRONLY | O_CREAT | O_TRUNC,0666);
            dup2(fd,1); //写入
        }
        else if(redir_type ==REDIR_APPEND)
        {
            int fd = open(filename,O_WRONLY | O_CREAT | O_APPEND,0666);
            dup2(fd,1); //追加
        }

        //直接执行程序替换
        execvp(argv[0],argv);

        exit(168); //替换失败后返回
    }

    //父进程等待子进程终止
    int status =0;
    waitpid(id,&status,0);
    exit_code=WEXITSTATUS(status);//更新退出码
    if(WIFEXITED(status))
    {
         if(exit_code == 168)
        printf("%s: Error - %s\n", argv[0], "The directive is not yet defined");
    }
    else
      printf("process run fail! [code_dump]:%d [exit_signal]:%d\n", (status >> 7) & 1, status & 0x7F);  //子进程异常终止的情况
  }
    return 0;
}

